﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
using System.Web.Script.Serialization;

namespace WebToolService
{
    public static class DataHelper
    {
        #region String
        public static string ToStringN(this object para)
        {
            return para == null ? null : para.ToString();
        }
        #endregion
        #region To Int32
        public static int ToInt32(this object para)
        {
            int res = 0;
            int resTemp = 0;
            if (int.TryParse(para.ToStringN(), out resTemp))
            {
                res = resTemp;
            }
            return res;
        }
        public static int? ToInt32_N(this object para)
        {
            int? res = null;
            int resTemp = 0;
            if (int.TryParse(para.ToStringN(), out resTemp))
            {
                res = resTemp;
            }
            return res;
        }
        #endregion
        #region To Int32
        public static decimal ToDecimal(this object para)
        {
            decimal res = 0;
            decimal resTemp = 0;
            if (decimal.TryParse(para.ToStringN(), out resTemp))
            {
                res = resTemp;
            }
            return res;
        }
        public static decimal ToDecimal(this object para, decimal defaultValue)
        {
            decimal res = defaultValue;
            decimal resTemp = 0;
            if (decimal.TryParse(para.ToStringN(), out resTemp))
            {
                res = resTemp;
            }
            return res;
        }
        public static decimal? ToDecimal_N(this object para)
        {
            decimal? res = null;
            decimal resTemp = 0;
            if (decimal.TryParse(para.ToStringN(), out resTemp))
            {
                res = resTemp;
            }
            return res;
        }
        #endregion
        #region To Int16
        public static Int16 ToInt16(this object para)
        {
            Int16 res = 0;
            Int16 resTemp = 0;
            if (Int16.TryParse(para.ToStringN(), out resTemp))
            {
                res = resTemp;
            }
            return res;
        }
        public static Int16? ToInt16_N(this object para)
        {
            Int16? res = null;
            Int16 resTemp = 0;
            if (Int16.TryParse(para.ToStringN(), out resTemp))
            {
                res = resTemp;
            }
            return res;
        }
        #endregion
        #region Guid
        public static Guid ToGuid(this object para)
        {
            Guid res;
            Guid.TryParse(para.ToStringN(), out res);
            return res;
        }
        #endregion
        #region Bool
        public static bool ToBool(this object para)
        {
            bool res;
            bool.TryParse(para.ToStringN(), out res);
            return res;
        }
        #endregion
        #region ToClass
        public static T ToClass<T>(this string jsonString)
        {
            return new JavaScriptSerializer().Deserialize<T>(jsonString);
        }
        public static object ToClass(this string jsonString, Type targetType)
        {
            return new JavaScriptSerializer().Deserialize(jsonString, targetType);
        }
        public static string ToJson(this object paraIn)
        {
            return new JavaScriptSerializer().Serialize(paraIn);
        }
        #endregion
        #region Null Check
        public static TResult Get<TSource, TResult>(this TSource source, System.Linq.Expressions.Expression<Func<TSource, TResult>> expression)
        {
            Type typeDefaultValue = typeof(TResult);
            object defaultValue = GetDefaultValue(typeDefaultValue);

            var safeExp = System.Linq.Expressions.Expression.Lambda<Func<TSource, TResult>>(
                NullSafeEvalWrapper(expression.Body, System.Linq.Expressions.Expression.Constant(defaultValue, typeDefaultValue)),
                expression.Parameters[0]);

            var safeDelegate = safeExp.Compile();
            return safeDelegate(source);
        }
        private static Object GetDefaultValue(Type typeDefaultValue)
        {
            object res;
            if (IsNullable(typeDefaultValue))
            {
                res = null;
            }
            else
            {
                if (typeDefaultValue == typeof(char))
                {
                    res = '0';
                }
                else if (typeDefaultValue == typeof(string))
                {
                    res = null;
                }
                else if (typeDefaultValue == typeof(Guid))
                {
                    res = Guid.Empty;
                }
                else if (typeDefaultValue == typeof(DateTime))
                {
                    res = DateTime.MinValue;
                }
                else if (typeDefaultValue == typeof(Boolean))
                {
                    res = false;
                }
                else if (typeDefaultValue == typeof(int))
                {
                    res = 0;
                }
                else if (typeDefaultValue == typeof(decimal))
                {
                    res = 0;
                }
                else if (typeDefaultValue == typeof(double))
                {
                    res = 0;
                }
                else if (typeDefaultValue == typeof(float))
                {
                    res = 0;
                }
                else
                {
                    res = null;
                }
            }
            return res;
        }
        private static System.Linq.Expressions.Expression NullSafeEvalWrapper(System.Linq.Expressions.Expression expr, System.Linq.Expressions.Expression defaultValue)
        {
            System.Linq.Expressions.Expression obj;
            System.Linq.Expressions.Expression safe = expr;

            while (!IsNullSafe(expr, out obj))
            {
                var isNull = System.Linq.Expressions.Expression.Equal(obj, System.Linq.Expressions.Expression.Constant(null));

                safe =
                    System.Linq.Expressions.Expression.Condition
                    (
                        isNull,
                        defaultValue,
                        safe
                    );

                expr = obj;
            }
            return safe;
        }
        private static bool IsNullable(Type type)
        {
            return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
        }
        private static bool IsNullSafe(System.Linq.Expressions.Expression expr, out System.Linq.Expressions.Expression nullableObject)
        {
            nullableObject = null;

            if (expr is MemberExpression || expr is MethodCallExpression)
            {
                System.Linq.Expressions.Expression obj;
                MemberExpression memberExpr = expr as MemberExpression;
                MethodCallExpression callExpr = expr as MethodCallExpression;

                if (memberExpr != null)
                {
                    // Static fields don't require an instance
                    FieldInfo field = memberExpr.Member as FieldInfo;
                    if (field != null && field.IsStatic)
                        return true;

                    // Static properties don't require an instance
                    PropertyInfo property = memberExpr.Member as PropertyInfo;
                    if (property != null)
                    {
                        MethodInfo getter = property.GetGetMethod();
                        if (getter != null && getter.IsStatic)
                            return true;
                    }
                    obj = memberExpr.Expression;
                }
                else
                {
                    // Static methods don't require an instance
                    if (callExpr.Method.IsStatic)
                        return true;

                    obj = callExpr.Object;
                }

                // Value types can't be null
                if (obj.Type.IsValueType)
                    return true;

                // Instance member access or instance method call is not safe
                nullableObject = obj;
                return false;
            }
            return true;
        }
        #endregion
        #region ToDataTable
        public static DataTable ToDataTable<T>(this List<T> list)
        {
            return ToDataTable<T>(list, null);
        }
        private static Type Convert_Type_For_Column(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                type = Nullable.GetUnderlyingType(type);
            return type;
        }
        public static DataTable ToDataTable<T>(List<T> list, params string[] propertyName)
        {
            List<string> propertyNameList = new List<string>();
            if (propertyName != null)
                propertyNameList.AddRange(propertyName);

            DataTable result = new DataTable();
            if (list.Count > 0)
            {
                PropertyInfo[] propertys = list[0].GetType().GetProperties();
                foreach (PropertyInfo pi in propertys)
                {
                    if (propertyNameList.Count == 0)
                    {
                        result.Columns.Add(pi.Name, Convert_Type_For_Column(pi.PropertyType));
                    }
                    else
                    {
                        if (propertyNameList.Contains(pi.Name))
                            result.Columns.Add(pi.Name, pi.PropertyType);
                    }
                }

                for (int i = 0; i < list.Count; i++)
                {
                    ArrayList tempList = new ArrayList();
                    foreach (PropertyInfo pi in propertys)
                    {
                        if (propertyNameList.Count == 0)
                        {
                            object obj = pi.GetValue(list[i], null);
                            tempList.Add(obj);
                        }
                        else
                        {
                            if (propertyNameList.Contains(pi.Name))
                            {
                                object obj = pi.GetValue(list[i], null);
                                tempList.Add(obj);
                            }
                        }
                    }
                    object[] array = tempList.ToArray();
                    result.LoadDataRow(array, true);
                }
            }
            return result;
        }
        #endregion




        #region Attribute
        public static T GetAttribute<T>(this MemberInfo member, bool isRequired)
            where T : Attribute
        {
            var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault();

            if (attribute == null && isRequired)
            {
                throw new ArgumentException(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "The {0} attribute must be defined on member {1}",
                        typeof(T).Name,
                        member.Name));
            }

            return (T)attribute;
        }
        public static string DisplayName<T>(this Expression<Func<T, object>> propertyExpression)
        {
            var memberInfo = GetPropertyInformation(propertyExpression.Body);
            if (memberInfo == null)
            {
                throw new ArgumentException(
                    "No property reference expression was found.",
                    "propertyExpression");
            }

            var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false);
            if (attr == null)
            {
                return memberInfo.Name;
            }

            return attr.DisplayName;
        }
        public static MemberInfo GetPropertyInformation(Expression propertyExpression)
        {
            Debug.Assert(propertyExpression != null, "propertyExpression != null");
            MemberExpression memberExpr = propertyExpression as MemberExpression;
            if (memberExpr == null)
            {
                UnaryExpression unaryExpr = propertyExpression as UnaryExpression;
                if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Convert)
                {
                    memberExpr = unaryExpr.Operand as MemberExpression;
                }
            }

            if (memberExpr != null && memberExpr.Member.MemberType == MemberTypes.Property)
            {
                return memberExpr.Member;
            }

            return null;
        }
        public static object GetValue<TSource>(this TSource tSource, Expression<Func<TSource, object>> expression)
        {
            return expression.Compile()(tSource);
        }
        #endregion
    }
}